/****************************************************************
 *								*
 *	Copyright 2003, 2004 Sanchez Computer Associates, Inc.	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

#include "mdef.h"

#include "gtm_stdio.h"

#include "compiler.h"
#include "opcode.h"
#include "mdq.h"
#include "cgp.h"
#include "gtmdbglvl.h"
#include "cdbg_dump.h"
#include "emit_code.h"
#include "rtnhdr.h"
#include "obj_file.h"

#if defined(USHBIN_SUPPORTED) || defined(VMS)

GBLREF int4		curr_addr, code_size;
GBLREF char		cg_phase;	/* code generation phase */
GBLREF triple		t_orig;		/* head of triples */
GBLREF uint4		gtmDebugLevel;
GBLREF int4		codegen_padlen;	/* Pad to code segment to put on section boundary */
LITREF octabstruct	oc_tab[];	/* op-code table */

/* Certain triples need to have their expansions shrunk down to be optimal. Current
   triples that are eligible for shrinking are:

   <triples of type OCT_JUMP>
   OC_LDADDR
   OC_FORLOOP
   literal argument references (either one) for given triple

   Many of the jump triples start out life as long jumps because of the assumped jump offset
   in the first pass before the offsets are known. This is where we shrink them.

   For the literal referencess, if the offset into the literal table exceeds that which can be
   addressed by the immediate operand of a load address instruction, a long form is generated.
   But this long form is initially assumed. This is where we will shrink it if we can which
   should be the normal case.

*/

void shrink_trips(void)
{
	int		new_size, old_size, shrink;
	boolean_t	first_pass;
	triple		*ct;	/* current triple */


#ifdef DEBUG
	/* If debug and compiler debugging is enabled, run through the triples again to show where we are just
	   befire we modify them.
	*/
	if (gtmDebugLevel & GDL_DebugCompiler)
	{
		PRINTF(" \n\n\n\n************************************ Begin pre-shrink_trips dump *****************************\n");
		dqloop(&t_orig, exorder, ct)
		{
			PRINTF(" ************************ Triple Start **********************\n");
			cdbg_dump_triple(ct, 0);
		}
		PRINTF(" \n\n\n\n************************************ Begin shrink_trips scan *****************************\n");
	}
#endif

	first_pass = TRUE;
	assert(cg_phase == CGP_ADDR_OPT);	/* Follows address optimization phase */
	do
	{
		shrink = 0;
		dqloop(&t_orig, exorder, ct)
		{
			if (oc_tab[ct->opcode].octype & OCT_JUMP || ct->opcode == OC_LDADDR || ct->opcode == OC_FORLOOP)
			{
				old_size = ct->exorder.fl->rtaddr - ct->rtaddr;
				curr_addr = 0;
				if (ct->operand[0].oprval.tref->rtaddr - ct->rtaddr < 0)
				{
					ct->rtaddr -= shrink;
					trip_gen(ct);
				} else
				{
					trip_gen(ct);
					ct->rtaddr -= shrink;
				}
				new_size = curr_addr;		/* size of operand 0 */
				assert(old_size >= new_size);
				COMPDBG(if (0 != (old_size - new_size)) cdbg_dump_shrunk_triple(ct, old_size, new_size););
				shrink += old_size - new_size;
			} else if (first_pass && !(oc_tab[ct->opcode].octype & OCT_CGSKIP) &&
				   (litref_triple_oprcheck(&ct->operand[0]) || litref_triple_oprcheck(&ct->operand[1])))
			{
				old_size = ct->exorder.fl->rtaddr - ct->rtaddr;
				curr_addr = 0;
				trip_gen(ct);
				ct->rtaddr -= shrink;
				new_size = curr_addr;		/* size of operand 0 */
				assert(old_size >= new_size);
				COMPDBG(if (0 != (old_size - new_size)) cdbg_dump_shrunk_triple(ct, old_size, new_size););
				shrink += old_size - new_size;
			} else
			{
				ct->rtaddr -= shrink;
			}
			if (0 == shrink && OC_TRIPSIZE == ct->opcode)
			{	/* This triple is a reference to another triple whose codegen size we want to compute for an
				   argument to another triple. We will only do this on what appears to (thus far) be
				   the last pass through the triples).
				*/
				curr_addr = 0;
				trip_gen(ct->operand[0].oprval.tsize->ct);
				ct->operand[0].oprval.tsize->size = curr_addr;
			}
		}/* dqloop */
		code_size -= shrink;
		first_pass = FALSE;
	} while (0 != shrink);		/* Do until no more shrinking occurs */

	/* Now that the code has been strunk, we may have to adjust the pad length of the code segment. Compute
	   this by now subtracting out the size of the pad length from the code size and recomputing the pad length
	   and readjusting the code size. (see similar computation in code_gen().
	*/
	code_size -= codegen_padlen;
	codegen_padlen = PADLEN(code_size, SECTION_ALIGN_BOUNDARY);	/* Length to pad to align next section */
	code_size += codegen_padlen;

#ifdef DEBUG
	/* If debug and compiler debugging is enabled, run through the triples again to show what we
	   have done to them..
	*/
	if (gtmDebugLevel & GDL_DebugCompiler)
	{
		dqloop(&t_orig, exorder, ct)
		{
			PRINTF(" ************************ Triple Start **********************\n");
			cdbg_dump_triple(ct, 0);
		}
	}
#endif
}


/* Check if this operand or any it may be chained to are literals in the literal section (not immediate operands) */
boolean_t litref_triple_oprcheck(oprtype *operand)
{
	if (TRIP_REF == operand->oprclass)
	{	/* We are referring to another triple */
		if (OC_LIT == operand->oprval.tref->opcode)
			/* It is a literal section reference.. done */
			return TRUE;
		if (OC_PARAMETER == operand->oprval.tref->opcode)
		{	/* There are two more parameters to check. Call recursively */
			if (litref_triple_oprcheck(&operand->oprval.tref->operand[0]))
				return TRUE;
			return litref_triple_oprcheck(&operand->oprval.tref->operand[1]);
		}
	}
	return FALSE;	/* Uninteresting triple .. not our type */
}
#endif /* USHBIN_SUPPORTED or VMS */
